import { Database } from "./Database"

/**
 * 用于给 Database 下的函数标记为迁移函数
 * startVersion 与 endVersion 的差的绝对值必须等于 1
 *
 * @param startVersion 起始版本
 * @param endVersion 结束版本
 */
export function Migration(startVersion: number, endVersion: number): MethodDecorator {
  return (target, propertyKey, descriptor): void => {
    const functionValue = descriptor.value
    if (functionValue instanceof Function &&
      functionValue.length === 1 &&
      endVersion - startVersion === 1) {
      Migration.set(target, propertyKey, descriptor, { startVersion, endVersion })
    } else {
      throw new Error(
        `Invalid migration configuration for ${String(propertyKey)}:\n` +
          '1. Method signature violation: Migration handler must be a function with exactly one parameter of type RdbStoreHelper\n' +
          '2. Version range error: startVersion and endVersion must be consecutive integers (e.g. startVersion:2 → endVersion:3)'
      )
    }
  }
}

/**
 * Migration 注解相关
 */
export namespace Migration {
  export class Config {
    constructor(
      readonly startVersion: number,
      readonly endVersion: number
    ) {
    }
  }

  const MigrationKey = Symbol('@Migration')

  export function get(target: Function) {
    const value = Reflect.get(target, MigrationKey)
    if (value instanceof Config) {
      return value
    } else {
      return null
    }
  }

  export function set(
    target: Object,
    propertyKey: string | symbol,
    descriptor: TypedPropertyDescriptor<any>,
    value: { startVersion: number, endVersion: number }): boolean {
    if (target instanceof Database && target[propertyKey] === descriptor.value) {
      return Reflect.set(descriptor.value, MigrationKey, new Config(value.startVersion, value.endVersion))
    }
    return false
  }

  export function ownFunctions(target: Database): Function[] {
    return Object
      .entries(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(target)))
      .filter(([, desc]) => {
        const func = desc.value
        return func instanceof Function && get(func) instanceof Config
      })
      .map(([, desc]) => desc.value)
  }
}